package com.andy.blog.admin.service.impl;

import com.alibaba.fastjson2.JSONArray;
import com.andy.blog.admin.dto.DictTypeDTO;
import com.andy.blog.admin.dto.SysDictDataDTO;
import com.andy.blog.admin.service.SysDictBusService;
import com.andy.blog.common.constant.CacheConstants;
import com.andy.blog.common.core.redis.RedisCache;
import com.andy.blog.common.exception.BlogServiceException;
import com.andy.blog.common.page.PageUtilsDTO;
import com.andy.blog.common.utils.TransformUtils;
import com.andy.blog.dal.domain.SysDictDataDO;
import com.andy.blog.dal.domain.SysDictTypeDO;
import com.andy.blog.dal.service.SysDictDataDalService;
import com.andy.blog.dal.service.SysDictTypeDalService;
import com.andy.blog.dal.vo.DictTypeVO;
import com.andy.blog.dal.vo.SysDictDataVO;
import com.andy.blog.dal.vo.SysDictTypeVO;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.apache.commons.collections4.CollectionUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.stream.Collectors;

/**
 * Created by andy on 2023/7/20 17:09
 */
@Service
public class SysDictBusServiceImpl implements SysDictBusService {

    @Resource
    private SysDictDataDalService sysDictDataDalService;

    @Resource
    private SysDictTypeDalService dictTypeDalService;

    @Resource
    private RedisCache redisCache;

    /**
     * 项目启动时，初始化字典到缓存
     */
    @PostConstruct
    public void init() {
        loadingDictCache();
    }

    @Override
    public PageUtilsDTO<SysDictDataDTO> selectDictDataByPage(SysDictDataVO sysDictDataVO) {
        IPage<SysDictDataDO> page = sysDictDataDalService.selectListByPage(sysDictDataVO);
        return PageUtilsDTO.build(page.getTotal(), TransformUtils.transformList(page.getRecords(), SysDictDataDTO.class));
    }

    @Override
    public List<SysDictDataDTO> selectDictDataByCondition(SysDictDataVO sysDictDataVO) {
        List<SysDictDataDO> dictDataDOS = sysDictDataDalService.selectListByCondition(sysDictDataVO);
        return TransformUtils.transformList(dictDataDOS, SysDictDataDTO.class);
    }

    @Override
    public SysDictDataDTO selectById(Long id) {
        SysDictDataDO sysDictDataDO = sysDictDataDalService.selectById(id);
        return TransformUtils.transform(sysDictDataDO, SysDictDataDTO.class);
    }

    @Override
    public void insertDictData(SysDictDataVO sysDictDataVO) {

        sysDictDataDalService.insertDictData(sysDictDataVO);

        List<SysDictDataDO> dictDataDOS = sysDictDataDalService.selectListByCondition(SysDictDataVO.builder().status("0")
                .dictType(sysDictDataVO.getDictType()).build());
        redisCache.setCacheObject(CacheConstants.SYS_DICT_KEY + sysDictDataVO.getDictType(), dictDataDOS);
    }

    @Override
    public List<SysDictDataDTO> selectDictDataByType(String dictType) {

        JSONArray cacheObject = redisCache.getCacheObject(getCacheKey(dictType));
        if (Objects.isNull(cacheObject)) {
            return new ArrayList<>();
        }

        List<SysDictDataDTO> dictDatas = cacheObject.toList(SysDictDataDTO.class);
        if (CollectionUtils.isNotEmpty(dictDatas)) {
            return dictDatas;
        }

        List<SysDictDataDO> sysDictDataDOS = sysDictDataDalService.selectDictDataByType(dictType);

        if (CollectionUtils.isNotEmpty(sysDictDataDOS)) {
            redisCache.setCacheObject(getCacheKey(dictType), TransformUtils.transformList(sysDictDataDOS, SysDictDataDTO.class));
        }

        return dictDatas;
    }

    @Override
    public List<DictTypeDTO> selectDictTypeAll() {
        List<SysDictTypeDO> sysDictTypeDOS = dictTypeDalService.selectDictTypeAll();
        return TransformUtils.transformList(sysDictTypeDOS, DictTypeDTO.class);
    }

    @Override
    public void deleteByIds(Long[] dictIds) {
        for (Long dictId : dictIds) {
            SysDictTypeDO dictType = dictTypeDalService.selectDictTypeById(dictId);
            if (sysDictDataDalService.countDictDataByType(dictType.getDictType()) > 0) {
                throw new BlogServiceException(String.format("%1$s已分配,不能删除", dictType.getDictName()));
            }
            dictTypeDalService.deleteByPrimaryKey(dictId);
            // 删除指定字典缓存
            redisCache.deleteObject(getCacheKey(dictType.getDictType()));
        }
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void update(SysDictTypeVO sysDictTypeVO) {
        if (!dictTypeDalService.checkDictTypeUnique(sysDictTypeVO)) {
            throw new BlogServiceException("修改字典'" + sysDictTypeVO.getDictName() + "'失败，字典类型已存在");
        }

        SysDictTypeDO oldDict = dictTypeDalService.selectDictTypeById(sysDictTypeVO.getId());

        sysDictDataDalService.updateDictDataType(oldDict.getDictType(), sysDictTypeVO.getDictType());

        int row = dictTypeDalService.updateDictType(sysDictTypeVO);

        if (row > 0) {
            List<SysDictDataDO> dictDatas = sysDictDataDalService.selectDictDataByType(sysDictTypeVO.getDictType());
            redisCache.setCacheObject(getCacheKey(sysDictTypeVO.getDictType()), TransformUtils.transformList(dictDatas, SysDictDataDTO.class));
        }
    }

    @Override
    public void insertDictType(SysDictTypeVO sysDictTypeVO) {
        if (!dictTypeDalService.checkDictTypeUnique(sysDictTypeVO)) {
            throw new BlogServiceException("新增字典'" + sysDictTypeVO.getDictName() + "'失败，字典类型已存在");
        }
        int row = dictTypeDalService.insertDictType(sysDictTypeVO);
        if (row > 0) {
            redisCache.setCacheObject(getCacheKey(sysDictTypeVO.getDictType()), null);
        }
    }

    @Override
    public List<DictTypeDTO> export(DictTypeVO dictTypeVO) {
        List<SysDictTypeDO> sysDictTypeDOS = dictTypeDalService.selectDictTypeList(dictTypeVO);
        return TransformUtils.transformList(sysDictTypeDOS, DictTypeDTO.class);
    }

    @Override
    public PageUtilsDTO<DictTypeDTO> selectDateTypeByPage(DictTypeVO queryDTO) {
        IPage<SysDictTypeDO> page = dictTypeDalService.selectListByPage(queryDTO);
        return PageUtilsDTO.build(page.getTotal(), TransformUtils.transformList(page.getRecords(), DictTypeDTO.class));
    }

    @Override
    public void deleteDictDataByIds(Long[] dictCodes) {
        for (Long id : dictCodes) {
            SysDictDataDTO data = selectById(id);
            sysDictDataDalService.deleteById(id);
            List<SysDictDataDO> dictDatas = sysDictDataDalService.selectDictDataByType(data.getDictType());
            redisCache.setCacheObject(getCacheKey(data.getDictType()), TransformUtils.transformList(dictDatas, SysDictDataDTO.class));
        }
    }

    @Override
    public void resetDictCache() {
        clearDictCache();
        loadingDictCache();
    }

    @Override
    public DictTypeDTO selectDictTypeById(Long dictId) {
        SysDictTypeDO sysDictTypeDO = dictTypeDalService.selectDictTypeById(dictId);
        return TransformUtils.transform(sysDictTypeDO, DictTypeDTO.class);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void updateDictData(SysDictDataVO sysDictDataVO) {

        sysDictDataDalService.updateDictData(sysDictDataVO);

        List<SysDictDataDO> dictDatas = sysDictDataDalService.selectDictDataByType(sysDictDataVO.getDictType());
        redisCache.setCacheObject(getCacheKey(sysDictDataVO.getDictType()), TransformUtils.transformList(dictDatas, SysDictDataDTO.class));
    }

    @Override
    public void loadingDictCache() {
        SysDictDataVO dictData = new SysDictDataVO();
        dictData.setStatus("0");
        Map<String, List<SysDictDataDO>> dictDataMap = sysDictDataDalService.selectListByCondition(dictData)
                .stream()
                .collect(Collectors.groupingBy(SysDictDataDO::getDictType));

        for (Map.Entry<String, List<SysDictDataDO>> entry : dictDataMap.entrySet()) {
            List<SysDictDataDO> sysDictDataDOS = entry.getValue().stream()
                    .sorted(Comparator.comparing(SysDictDataDO::getDictSort))
                    .collect(Collectors.toList());
            redisCache.setCacheObject(getCacheKey(entry.getKey()), TransformUtils.transformList(sysDictDataDOS, SysDictDataDTO.class));
        }
    }

    @Override
    public void clearDictCache() {
        Collection<String> keys = redisCache.keys(CacheConstants.SYS_DICT_KEY + "*");
        redisCache.deleteObject(keys);
    }


    /**
     * 设置cache key
     *
     * @param configKey 参数键
     * @return 缓存键key
     */
    public static String getCacheKey(String configKey) {
        return CacheConstants.SYS_DICT_KEY + configKey;
    }
}
